let s:MSG = api#import('message')
let s:LOG = api#import('logger')
let s:engine = { "interval": 20, "repeat": -1, "tasks": [], "completed_tasks": [], }

function! s:engine.pop() dict
    if len(self.tasks) > 0 
        let l:task = remove(self.tasks, 0)
        return l:task
    else
        throw "No tasks are waiting."
    endif
endfunction

function! s:engine.append(task) dict
    call add(self.tasks, a:task)
    return self
endfunction

function! s:engine.step() dict
    if len(self.tasks) > 0
        let self.start_time = reltime()
        let l:task = self.pop()
        let l:task = l:task.run(self)
        if !empty(l:task) && (!has_key(l:task, 'finished') || !l:task.finished)
            let self.completed_tasks = add(get(self, 'completed_tasks', []), l:task)
        endif
        return self.callback(l:task)
    endif
endfunction

function! s:engine.callback(task) dict
    let l:seconds = reltimefloat(reltime(self.start_time))
    if l:seconds * 1000  >= self.interval
        let l:interval = float2nr(ceil(l:seconds * 1000))
        call s:MSG.debug("interval set to " . l:interval)
        call self.set('interval', l:interval)
    endif
    let l:waiting = len(self.tasks)
    let l:completed = len(self.completed_tasks)
    let l:status = 'complete tasks: ' . l:completed . '; waiting tasks: ' . l:waiting
    call s:MSG.debug(l:status)
endfunction

function! s:engine.stop() dict
    if has_key(self, 'timer') && self.timer != 0
        call timer_stop(self.timer)
        let self.timer = 0
    endif
    return self
endfunction

function! s:engine.start() dict
    let l:options = { "interval": self.interval, "repeat": self.repeat }
    let self.timer = timer_start(l:options.interval, { id -> self.step() }, 
                \ { "repeat" : l:options.repeat })
    return self
endfunction

function! s:engine.set(key, value) dict
    let self[a:key] = a:value
    if a:key == 'interval'
        call self.stop()
        call self.start()
    endif
    return self
endfunction

function! api#engine#get() abort
    return s:engine
endfunction

